package com.zyz.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zyz.api.BlogServiceApi;
import com.zyz.api.pojo.*;
import com.zyz.common.exception.BlogException;
import com.zyz.common.pojo.Result;
import com.zyz.common.utils.*;
import com.zyz.user.constant.Constant;
import com.zyz.user.dataobject.UserInfo;
import com.zyz.user.mapper.UserInfoMapper;
import com.zyz.user.service.UserService;
import jakarta.validation.constraints.NotBlank;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.constraints.Length;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private BlogServiceApi blogServiceApi;

    @Autowired
    private Redis redis;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    private static final int REDIS_EXPIRE_TIME = 60 * 60 * 24 * 7; // 一周

    @Override
    public UserLoginResponse login(UserInfoRequest user) {
        //验证账号密码是否正确
        UserInfo userInfo = queryUserInfo(user.getUserName());
        if (userInfo==null || userInfo.getId()==null){
            throw new BlogException("用户不存在");
        }

        if (!SecurityUtil.verify(user.getPassword(),userInfo.getPassword())){
            throw new BlogException("用户密码不正确");
        }
        //账号密码正确的逻辑
        Map<String,Object> claims = new HashMap<>();
        claims.put("id", userInfo.getId());
        claims.put("name", userInfo.getUserName());

        String jwt = JWTUtils.genJwt(claims);
        return new UserLoginResponse(userInfo.getId(), jwt);
    }



    @Override
    public UserInfoResponse getUserInfo(Integer userId) {
        UserInfoResponse userInfoResponse = new UserInfoResponse();
        UserInfo userInfo = selectUserInfoById(userId);
        BeanUtils.copyProperties(userInfo, userInfoResponse);
        return userInfoResponse;
    }

    @Override
    public UserInfoResponse selectAuthorInfoByBlogId(Integer blogId) {
        UserInfoResponse userInfoResponse = new UserInfoResponse();
        // openfeign 远程调用
        //1. 根据博客ID, 获取作者ID
        Result<BlogInfoResponse> resultBlogInfo = blogServiceApi.getBlogDeatail(blogId);

        //2. 根据作者ID, 获取作者信息
        if (resultBlogInfo == null || resultBlogInfo.getData() == null){
            throw new BlogException("博客不存在");
        }
        UserInfo userInfo = selectUserInfoById(resultBlogInfo.getData().getUserId());
        BeanUtils.copyProperties(userInfo, userInfoResponse);
        return userInfoResponse;
    }

    /**
     *
     * @param userInfoRegisterRequest
     * @return 返回注册用户的userID
     */
    @Override
    public Integer register(UserInfoRegisterRequest userInfoRegisterRequest) {
        // 1.校验用户名不能重复  2.密码格式、邮箱格式、url格式
        checkUserInfo(userInfoRegisterRequest);
        // 3.用户注册
        UserInfo userInfo = new UserInfo();
        userInfo.setUserName(userInfoRegisterRequest.getUserName());
        String userPasswordMd5 = SecurityUtil.encrypt(userInfoRegisterRequest.getPassword());
        userInfo.setPassword(userPasswordMd5);
        userInfo.setGithubUrl(userInfoRegisterRequest.getGithubUrl());
        userInfo.setEmail(userInfoRegisterRequest.getEmail());

        try {
            int row = userInfoMapper.insert(userInfo);
            if (row != 1) {
                throw new BlogException("register新增数据库失败");
            }
            // 将数据放入redis中
            String userJson = JsonUtil.toJson(userInfo);
            redis.setAndExpire(Redis.buildKey("user",userInfo.getUserName()),userJson,REDIS_EXPIRE_TIME);
            // 发送消息到rabbitmq中
            userInfo.setPassword("");
            rabbitTemplate.convertAndSend(Constant.USER_EXCHANGE,"",JsonUtil.toJson(userInfo));
            return userInfo.getId();
        } catch (Exception e) {
            log.error("用户注册失败，err：{}",e.getMessage());
            throw new BlogException("用户注册失败");
        }
    }



    private void checkUserInfo(UserInfoRegisterRequest userInfoRegisterRequest) {
        // 校验用户名不能重复
        UserInfo userInfo = selectUserInfoByName(userInfoRegisterRequest.getUserName());
        if (userInfo != null) {
            throw new BlogException("注册用户，用户名已存在 UserName：" + userInfo.getUserName());
        }
        // 邮箱格式，github格式
        if(!RegexUtil.checkMail(userInfoRegisterRequest.getEmail())) {
            throw new BlogException("邮箱格式不合法");
        }
        if (userInfoRegisterRequest.getGithubUrl() != null &&
                !RegexUtil.checkURL(userInfoRegisterRequest.getGithubUrl())) {
            throw new BlogException("GitHubUrl不合法");
        }
    }

    private UserInfo queryUserInfo(String userName) {
        // 先从redis中读取数据，如果不存在再从MySQL中读取数据
        String key = Redis.buildKey("user", userName);
        boolean exists = redis.exists(key);
        if (exists) {
//            log.info("从redis中获取数据");
            // redis中存在
            String userJson = redis.get(key);
            UserInfo userInfo = JsonUtil.parseJson(userJson, UserInfo.class);
            return userInfo==null?selectUserInfoByName(userName):userInfo;
        } else {
            // redis中不存在
            UserInfo userInfoByMysql = selectUserInfoByName(userName);
            // 存入到redis中
            redis.setAndExpire(Redis.buildKey("user",userInfoByMysql.getUserName()),JsonUtil.toJson(userInfoByMysql),REDIS_EXPIRE_TIME);
            return userInfoByMysql;
        }
    }

    public UserInfo selectUserInfoByName(String userName) {
        return userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfo>()
                .eq(UserInfo::getUserName, userName).eq(UserInfo::getDeleteFlag, 0));
    }
    private UserInfo selectUserInfoById(Integer userId) {
        return userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfo>()
                .eq(UserInfo::getId, userId).eq(UserInfo::getDeleteFlag, 0));
    }

}
